﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using Myotragus.Data;
using Myotragus.Data.Containers;
using Myotragus.Data.Containers.Windsor;
using Myotragus.Data.Domain;
using Myotragus.Data.Emit;
using Myotragus.Data.Hbm;
using Myotragus.Data.Registration;
using Myotragus.Data.Util;
using Myotragus.Store.Model.Domain;
using NHibernate.Cfg;

namespace Myotragus.Store.Model.Helpers
{
  public static class Core
  {
    public static IServicesContainer Container { get; private set; }
    public static Configuration Configuration { get; private set; }
    
    public static void Register(Configuration configuration, IEnumerable<Type> entities, 
      IEnumerable<string> mappings)
    {
      RegisterServices();
      RegisterEntities();
      SetupSession(entities, mappings, configuration);
    }

    private static void SetupSession(IEnumerable<Type> entities, IEnumerable<string> mappings, 
      Configuration configuration)
    {
      var generatorTypes = new[]
      {
        new {Type = typeof(ILoadEntityGenerator), Hbm=typeof(ILoadEntityHbmGenerator)},
        new {Type = typeof(ISaveEntityGenerator), Hbm=typeof(ISaveEntityHbmGenerator)},
        new {Type = typeof(IDefaultsLoadEntityGenerator), Hbm=typeof(IDefaultsLoadEntityHbmGenerator)},
        new {Type = typeof(IDefaultsSaveEntityGenerator), Hbm=typeof(IDefaultsSaveEntityHbmGenerator)},
      };

      Configuration = configuration;
      Configuration.Configure();
      Configuration.AddAssembly(typeof(ILocalizableText).Assembly);

      foreach (var pair in generatorTypes)
      {
        var genType = (ITypeGenerator)Container.Resolve(pair.Type);
        var genHbm = (IHbmGenerator)Container.Resolve(pair.Hbm);

        foreach (var ent in entities)
        {
          Configuration.AddAssembly(ent.Assembly);

          var type = genType.GetImplementation(ent);
          Configuration.AddXml(genHbm.GenerateMapping(type, ent));
        }
      }

      var resolver = Container.Resolve<IResolutionHbmGenerator>();
      foreach (var hbm in mappings.Select(m => resolver.Generate(m)))
        Configuration.AddXml(hbm);

      var factory = Container.Resolve<ISessionFactoryAdapter>();
      factory.SetFactory(Configuration.BuildSessionFactory());
    }

    private static void RegisterEntities()
    {
      var entities = EntitiesInterfaces.FromAssemblyContaining<IImplemented>();
      var toGenerate = entities.NotImplementing<IImplemented>();

      var generators = new[]
      {
        typeof(IAutoProperties),
        typeof(ILoadEntityGenerator),
        typeof(ISaveEntityGenerator),
        typeof(IDefaultsLoadEntityGenerator),
        typeof(IDefaultsSaveEntityGenerator),
      };

      var registrarType = Container.Resolve<IEntitiesRegistrar>();

      foreach (var type in generators
        .Select(genType => (ITypeGenerator)Container.Resolve(genType))
        .SelectMany(genType => toGenerate.Select(t => genType.GetImplementation(t))))
      {
        registrarType.RegisterForAll(type);
      }

      var implementedEntities = EntitiesImplementation.FromAssemblyContaining<IImplemented>()
        .Implementing<IImplemented>();
      registrarType.Register(implementedEntities);

      Entities = registrarType;
    }

    public static IEntitiesContainer Entities { get; private set; }

    private static void RegisterServices()
    {
      var container = ContainerHelper.CreateContainer();

      container.Register(ServicesImplementation.FromAssemblyContaining<IEntity>());
      container.Register(ServicesImplementation.FromAssembly(Assembly.GetExecutingAssembly()));

      Container = container;
    }
  }
}
